package Day013_线程安全.demo01_线程池;

import java.util.concurrent.*;

/**
 * @author zhangyan
 * @date 2020/5/4 9:07
 */

/*
    线程池：是一个容纳多个线程的容器，其中的线程可以反复使用，省去了频繁创建线程对象的操作，无需反复创建线程而消耗过多资源。

    线程池能够带来三个好处：
            1. 降低资源消耗。减少了创建和销毁线程的次数，每个工作线程都可以被重复利用，可执行多个任务。
            2. 提高响应速度。当任务到达时，任务可以不需要的等到线程创建就能立即执行。
            3. 提高线程的可管理性。可以根据系统的承受能力，调整线程池中工作线线程的数目，防止因为消耗过多的内存，
               而把服务器累趴下(每个线程需要大约1MB内存，线程开的越多，消耗的内存也就越大，最后死机)。

    线程池的使用:
            Java里面线程池的顶级接口是 java.util.concurrent.Executor ，严格意义上讲 Executor 并不是一个线程池，而只是一个执行线程的工具
            真正的线程池接口是 java.util.concurrent.ExecutorService

        Executors 类中有个创建线程池的方法如下：
                    public static ExecutorService newFixedThreadPool(int nThreads) ：返回线程池对象。
                    (创建的是有界线程池,也就是池中的线程个数可以指定最大数量)

        使用线程池对象的方法如下：
                    public Future<?> submit(Runnable task) :通过参数传入任务,获取线程池中的某一个线程对象，并执行
                    public <T> Future<T> submit(Callable<T> task) :通过参数传入任务,获取线程池中的某一个线程对象，并执行
                        Runnable:线程执行的任务    void run(); 无参无返回值
                        Callable:线程执行的任务    V  call(); 无参有返回值
                            如果执行任务之后不需要返回数据,就Runnable
                            如果执行任务之后需要返回数据,就Callable
                        Future接口：用来记录线程任务执行完毕后产生的结果。
                            V  get(); 可以获取线程执行完任务后返回的结果

        使用线程池中线程对象的步骤：
                    1. 创建线程池对象
                    2. 创建任务
                    3. 创建并执行任务
                    4. 关闭线程池(一般不做)
*/

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    //    //1.Runnable方式
    //
    //    //创建线程池-初始化线程
    //    ExecutorService es = Executors.newFixedThreadPool(3); // 3条线程
    //    //创建任务
    //    MyRunnable mr = new MyRunnable();
    //    //提交并执行任务
    //    es.submit(mr);
    //    es.submit(mr);
    //    es.submit(mr);
    //    es.submit(mr);

        //2.Callable方式

        //创建线程池-初始化线程
        ExecutorService es1 = Executors.newFixedThreadPool(3);
        //创建任务
        MyCallable mc = new MyCallable();
        //提交并执行任务
        Future<String> future = es1.submit(mc);
        es1.submit(mc);
        es1.submit(mc);
        es1.submit(mc);
        es1.submit(mc);
        System.out.println("获取任务执行完毕后的结果:" + future.get());

    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":开始实现Runnable的方式...");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +  ":执行完毕.");
    }
}

class MyCallable implements Callable<String>{
    @Override
    public String call() throws InterruptedException { // 返回值String/Integer....
        //线程需要执行的任务
        System.out.println(Thread.currentThread().getName() + ":开始执行Callable的方式...");
        Thread.sleep(3000); // 可以抛,可以try
        System.out.println(Thread.currentThread().getName() + ":执行完毕.");
        //返回执行结果
        return "五一快乐";
    }
}